﻿using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Formats.Biff.Tokens;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    internal class CF12Record : WorksheetLevelRecord
    {
        // private byte _ae;
        private byte _cp;
        private CF12Type _ct;
        private CFParsedFormula _fmlaActive;
        private CFTemplate _icfTemplate;
        private ushort _ipriority;
        private CFBase _rgbCt;
        private CFExTemplateParams _rgbTemplateParms;
        private CFParsedFormulaNoCCE _rgce1;
        private CFParsedFormulaNoCCE _rgce2;
        private DXFN12 dxf;
        private FrtRefHeader frtRefHeader = new FrtRefHeader(0x087A);
        private bool unused1 = true;
        private bool fStopIfTrue;
        private bool reserved1;
        private bool unused2;

        public CF12Record(ConditionalFormattingRule rule, BiffWriterWorkbookContext context, Cell referenceCell)
        {
            Contract.Ensures(_icfTemplate != (CFTemplate) 0xFFFF);
            _icfTemplate = (CFTemplate)(ushort)0xFFFF;
            _ipriority = (ushort) rule.Priority;
            _fmlaActive = new CFParsedFormula();
            if (rule is ColorScaleFormattingRule)
            {
                _ct = CF12Type.ColorScale;
                var colorScaleFormattingRule = (ColorScaleFormattingRule) rule;
                _rgbCt = new CFGradient(colorScaleFormattingRule.ColorScale);
                this._icfTemplate = CFTemplate.ColorScaleFormatting;
                _rgbTemplateParms = new CFExDefaultTemplateParams();
            }
            else if (rule is DataBarFormattingRule)
            {
                _ct = CF12Type.DataBar;
                var dataBarFormattingRule = (DataBarFormattingRule)rule;
                _rgbCt = new CFDatabar(dataBarFormattingRule.DataBar);
                this._icfTemplate = CFTemplate.DataBarFormatting;
                _rgbTemplateParms = new CFExDefaultTemplateParams();
            }
            else if (rule is IconSetFormattingRule)
            {
                _ct = CF12Type.IconSet;
                var iconSetFormattingRule = (IconSetFormattingRule)rule;
                _rgbCt = new CFMultistate(iconSetFormattingRule.IconSet);
                this._icfTemplate = CFTemplate.IconSetFormatting;
                _rgbTemplateParms = new CFExDefaultTemplateParams();
            }
            else
                throw new NotImplementedException();
        }

        public CF12Record()
        {
        }


        internal override BiffRecordType RecordType
        {
            get { return BiffRecordType.CF12; }
        }

        protected override void ReadContent(BiffReaderContext context, int length)
        {
#if DEBUG
            var position = context.Reader.BaseStream.Position;
#endif
            ReadReader(context.Reader);
#if DEBUG
            var position2 = context.Reader.BaseStream.Position;
            Contract.Assert(position2 - position == length);
#endif
        }

        private void ReadReader(BinaryReader reader)
        {
#if DEBUG
            long position = reader.BaseStream.Position;
            ReadWriteChecker.CheckClass((t, r) => t.ReadReader(r),
                                        (t, w) => t.WriteWriter(w), reader, this);
#endif
            frtRefHeader.Read(reader);
            Contract.Assert(frtRefHeader.rt == 0x087A);
            _ct = (CF12Type) reader.ReadByte();
            _cp = reader.ReadByte();
            ushort cce1 = reader.ReadUInt16();
            ushort cce2 = reader.ReadUInt16();
            dxf.Read(reader);
            if (cce1 > 0)
            {
                _rgce1 = new CFParsedFormulaNoCCE();
                _rgce1.ReadMe(new FormulaReadContext(reader, FormulaReadContextType.Default), cce1);
            }
            else
                _rgce1 = null;
            if (cce2 > 0)
            {
                _rgce2 = new CFParsedFormulaNoCCE();
                _rgce2.ReadMe(new FormulaReadContext(reader, FormulaReadContextType.Default), cce2);
            }
            else
            {
                _rgce2 = null;
            }
            _fmlaActive = new CFParsedFormula();
            _fmlaActive.Read(new FormulaReadContext(reader, FormulaReadContextType.Default));
            var ae = reader.ReadByte();
            this.unused1 = (ae & 0x01) != 0;
            this.fStopIfTrue = (ae & 0x02) != 0;
            this.reserved1 = (ae & 0x04) != 0;
            this.unused2     = (ae & 0x08) != 0;
            Contract.Assert(this.Ae == ae);
            _ipriority = reader.ReadUInt16();
            _icfTemplate = (CFTemplate) reader.ReadUInt16();
            byte cbTemplateParm = reader.ReadByte();
            _rgbTemplateParms = CFExTemplateParams.Create( _icfTemplate);
#if DEBUG
            Debug.WriteLine("before _rgbTemplateParms.Read, " + (reader.BaseStream.Position - position) + " " + _icfTemplate);
#endif
            _rgbTemplateParms.Read(reader);
            switch (_ct)
            {
                case CF12Type.Comparison:
                case CF12Type.Formula:
                    _rgbCt = new CFEmpty();
                    break;
                case CF12Type.ColorScale:
                    _rgbCt = new CFGradient();
                    break;
                case CF12Type.DataBar:
                    _rgbCt = new CFDatabar();
                    break;
                case CF12Type.Filter:
                    _rgbCt = new CFFilter();
                    break;
                case CF12Type.IconSet:
                    _rgbCt = new CFMultistate();
                    break;
                default:
                    throw new NotSupportedException();
            }
            _rgbCt.Read(reader);
        }

        protected byte Ae
        {
            get { byte result = 0;
            if (this.unused1) result |= 0x01;
            if (this.fStopIfTrue) result |= 0x02;
            if (this.reserved1) result |= 0x04;
            if (this.unused2) result |= 0x08;
                return result;
            }
        }

        private void WriteWriter(BinaryWriter writer)
        {
            frtRefHeader.Write(writer);
            writer.Write((byte) _ct);
            writer.Write(_cp);

            ushort ui = (_rgce1 != null ? _rgce1.TotalTokenByteLength(FormulaReadContextType.Default) : (ushort) 0);
            writer.Write(ui);
            ui = (_rgce2 != null ? _rgce2.TotalTokenByteLength(FormulaReadContextType.Default) : (ushort) 0);
            writer.Write(ui);
            dxf.Write(writer);
            if (_rgce1 != null) _rgce1.WriteMe(new FormulaWriteContext(writer, FormulaReadContextType.Default));
            if (_rgce2 != null)
                _rgce2.WriteMe(new FormulaWriteContext(writer, FormulaReadContextType.Default));

            _fmlaActive.Write(new FormulaWriteContext(writer, FormulaReadContextType.Default));
            writer.Write(Ae);
            writer.Write(_ipriority);
            writer.Write((ushort)_icfTemplate);
            byte cbTemplateParm = 16;
            writer.Write(cbTemplateParm);
            _rgbTemplateParms.Write(writer);
            _rgbCt.Write(writer);
        }

        internal override void Apply(BiffReaderWorksheetContext context)
        {
            ((CondFmt12Record) context.CurrentCondFmtRecord).CF12Records.Add(this);
        }

        internal override void WriteContent(BiffWriterWorkbookContext context)
        {
            WriteWriter(context.Writer);
        }
        public ConditionalFormattingRule ToRule(BiffReaderWorksheetContext context)
        {
            var result = ToNakedRule(context);
            result.Priority = _ipriority;
            return result;
        }
        private ConditionalFormattingRule ToNakedRule(BiffReaderWorksheetContext context)
        {
            switch (_ct)
            {
                case CF12Type.ColorScale:
                    var gradient = (CFGradient) _rgbCt;
                    ColorScale cs = gradient.ToColorScale(context);
                    return new ColorScaleFormattingRule(cs.Colors);
                case CF12Type.DataBar:
                    var cfDataBar = (CFDatabar) _rgbCt;
                    DataBar dataBar = cfDataBar.ToDataBar(context);
                    return new DataBarFormattingRule(dataBar);
                case CF12Type.IconSet:
                    var cfIconSet = (CFMultistate) _rgbCt;
                    IconSet iconSet = cfIconSet.ToIconSet(context);
                    return new IconSetFormattingRule(iconSet);
                case CF12Type.Filter:
                    var cfFilter = (CFFilter) _rgbCt;
                    Filter fister = cfFilter.ToFilter();
                    throw new NotImplementedException();
                case CF12Type.Formula:
                case CF12Type.Comparison:
                    throw new NotImplementedException();
                default:
                    throw new NotSupportedException();
            }
        }

        #region Nested type: CF12Type

        private enum CF12Type : byte
        {
            Comparison = 1,
            Formula = 2,
            ColorScale = 3,
            DataBar = 4,
            Filter = 5,
            IconSet = 6
        }

        #endregion
    }
}